from django.db import models
from django.db.transaction import commit_on_success

from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

import datetime
from dateutil import rrule

#from taggit.managers import TaggableManager


class Account(models.Model):
    user = models.ForeignKey(User)
    #number = models.CharField(max_length=80, primary_key=True)
    #balance = MoneyField(max_digits=12, decimal_places=2)
    name = models.CharField(max_length=250)
    type = models.CharField(max_length=250,blank=True)
    balance = models.FloatField(default=0)
    description = models.TextField(blank=True)
    
    updated = models.DateTimeField(auto_now_add=True, auto_now=True, editable=False)
    created = models.DateTimeField('date create',auto_now_add=True, editable=False)
  
  
      
    @commit_on_success
    def add_transaction(self, name, amount, category, date, description):
        if name != "Open account" :
            self.balance += amount
        if not category or category is None :
            category = Category.get(cid_exact=category)
        transaction = self._create_transaction(name, amount, category, date, description)
        self.save()
        transaction.save()
        
    @commit_on_success
    def move_money(self, to_account, amount, category, description):
        #TODO: does need to check self.user and to_account.user is the same ?
        self.balance -= amount
        to_account.balance += amount
        
        transaction = self._create_transaction(0-amount, category, description)
        self.save()
        transaction.save()
        
        to_transaction = to_account._create_transaction(amount, category, description)
        to_account.save()
        to_transaction.save()
    
    @commit_on_success
    def _create_transaction(self, name, amount, category, date, description):
        # print "Create Transaction ----- \namount: %s\n category: %s\n tag: %s\n description: %s\n" % (str(amount), str(category), str(tags), str(description))
        if amount is None :
            raise Exception("Invalid amount")
        transaction = Transaction(user=self.user,
                           account=self,
                           date=date,
                           name=name,
                           amount=amount, 
                           category=category,
                           description=description)
        transaction.save()
        return transaction
    
    def __unicode__(self):
        return self.name
      
CATEGORY_TYPE = ((0,"Income"),(1,"Expense"))
class Category(models.Model):
    user = models.ForeignKey(User)
    name = models.CharField(max_length=500)
    type = models.IntegerField(choices=CATEGORY_TYPE)
    tax = models.IntegerField(default=0)
    #tags = TaggableManager(blank=True)
    description = models.TextField(blank=True)
    
    updated = models.DateTimeField(auto_now_add=True, auto_now=True, editable=False)
    created = models.DateTimeField(auto_now_add=True, editable=False)
    
    def __unicode__(self):
        return self.name

class TransactionModel(models.Model):
    user = models.ForeignKey(User)
    account = models.ForeignKey(Account, blank=True, null=True, on_delete=models.SET_NULL)
    name = models.CharField(max_length=500)
    category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL)
    amount = models.FloatField()
    description = models.TextField(blank=True)
    date = models.DateTimeField()
    
    updated = models.DateTimeField(auto_now_add=True, auto_now=True, editable=False)
    created = models.DateTimeField(auto_now_add=True, editable=False)
      
class Transaction(TransactionModel):
    pass
  
# ===================================================================
RRULE_WEEKDAYS = {"MO":0,"TU":1,"WE":2,"TH":3,"FR":4,"SA":5,"SU":6}

freqs = (   ("YEARLY",   _("Yearly")),
            ("MONTHLY",  _("Monthly")),
            ("WEEKLY",   _("Weekly")),
            ("DAILY",    _("Daily")),
            ("HOURLY",   _("Hourly")),
            ("MINUTELY", _("Minutely")),
            ("SECONDLY", _("Secondly")))

class Rule(models.Model):
    """
    This defines a rule by which an event will recur. This is defined by the
    rrule in the dateutil documentation.

    * name - the human friendly name of this kind of recursion.
    * description - a short description describing this type of recursion.
    * frequency - the base recurrence period
    * param - extra params required to define this type of recursion. The params
      should follow this format:

        param = [rruleparam:value;]*
        rruleparam = see list below
        value = int[,int]*

      The options are: (documentation for these can be found at
      http://labix.org/python-dateutil#head-470fa22b2db72000d7abe698a5783a46b0731b57)
        ** count
        ** bysetpos
        ** bymonth
        ** bymonthday
        ** byyearday
        ** byweekno
        ** byweekday
        ** byhour
        ** byminute
        ** bysecond
        ** byeaster
    """
    user = models.ForeignKey(User)
    # description = models.TextField(_("description"))
    frequency = models.CharField(_("frequency"), choices=freqs, max_length=10)
    params = models.TextField(_("params"), null=True, blank=True)

    class Meta:
        verbose_name = _('rule')
        verbose_name_plural = _('rules')

    def parse_param(self, param_value):
        param = param_value.split('(',1)[0]
        if param in RRULE_WEEKDAYS:
            try:
                return eval("rrule.%s" % param_value)
            except ValueError:
                raise ValueError('rrule parameter improperly formatted. Error on: %s' % param_value)
        try:
            return int(param_value)
        except ValueError:
            raise ValueError('rrule parameter should be integer or weekday constant (e.g. MO, TU, etc.). Error on: %s' % param_value)

    def get_params(self):
        """
        >>> rule = Rule(params = "count:1;bysecond:1;byminute:1,2,4,5")
        >>> rule.get_params()
        {'count': 1, 'byminute': [1, 2, 4, 5], 'bysecond': 1}
        """
        if self.params is None:
            return {}
        params = self.params.split(';')
        param_dict = []
        for param in params:
            if param.strip() == "":
                continue # skip blanks
            param = param.split(':')
            if len(param) == 2:
                param = (str(param[0]).strip(), [self.parse_param(p.strip()) for p in param[1].split(',')])
                if len(param[1]) == 1:
                    param = (param[0], param[1][0])
                param_dict.append(param)
        return dict(param_dict)

    def get_rrule_object(self,start):
        params = self.get_params()
        frequency = rrule.__dict__[self.frequency]
        return rrule.rrule(frequency, dtstart=start, **params)
                   
    def __unicode__(self):
        """Human readable string for Rule"""
        return self.user.username + self.frequency + self.params


            
class Bill(TransactionModel):
    start = models.DateTimeField()
    end = models.DateTimeField(null = True, blank = True)
    last = models.DateTimeField()
    rule = models.ForeignKey(Rule, null = True, blank = True, verbose_name=_("rule"), help_text=_("Select '----' for a one time only event."))
    
    def __unicode__(self):
        return self.name  
    
    def get_occurrences(self, start, end):
        return self.rule.get_rrule_object(self.start).between(start, end, inc=True)
    
    def get_occurrence(self, date):
        return self.rule.get_rrule_object(self.start).after(date, inc=True)
    
    def set_rule(self, frequency, params):
        if self.rule :
            self.rule.delete()
        rule =  Rule(user=self.user,
                     frequency=frequency.upper(),
                     params=params)
        rule.save()              
        self.rule = rule
        self.save()

class Budget(models.Model):
    user = models.ForeignKey(User)
    #name = models.CharField(max_length=500)
    account = models.ForeignKey(Account)
    category = models.ForeignKey(Category)
    estimate = models.FloatField()
    rule = models.ForeignKey(Rule, null = True, blank = True, verbose_name=_("rule"), help_text=_("Select '----' for a one time only event."))
    
    '''
    def delete(self):
        """
        Override default model method so an relate is not deleted
        when its budget is deleted.
        """
        self.account.clear()
        self.category.clear()
        super(Budget, self).delete()
    ''' 
    def __unicode__(self):
        return u"%s - %s" % (self.category.name, self.amount)
    
    def yearly_estimated(self):
        return self.estimate * 12
    
    def actual_transactions(self, start_date, end_date):
        return Transaction.objects.filter(user=self.user, account=self.account, category=self.category, date__range=(start_date, end_date)).order_by('date')
    
    def actual_amount(self, start_date, end_date):
        total = 0.0
        for transaction in self.actual_transactions(start_date, end_date):
            total += transaction.amount
        if self.category.type == 1:
            total = 0 - total
        return total
    
    def get_monthusage(self):
        today = datetime.date.today()
        return self.actual_amount(today.replace(day=1), today)
      
    def get_monthusagepercent(self):
        return (self.get_monthusage()*100.00)/self.estimate
      
